package gin

import (
	"context"
	"net"
	"net/http"
	"reflect"

	"gitee.com/zawei/yggdrasil/pkg/log"
	"gitee.com/zawei/yggdrasil/pkg/transport"
	"gitee.com/zawei/yggdrasil/pkg/transport/server"
	"gitee.com/zawei/yggdrasil/pkg/utils/xnet"
	"github.com/gin-gonic/gin"
)

const SupportPackageIsVersion1 = true

type MethodDesc struct {
	MethodType string
	MethodName string
	Path       string
	Handler    func(svr interface{}, c *gin.Context)
}

type ServiceDesc struct {
	ServiceName string
	HandlerType interface{}
	Methods     []MethodDesc
}

// Server ...
type Server struct {
	*gin.Engine
	Server   *http.Server
	config   *Config
	listener net.Listener
	info     *server.Info
}

func newServer(config *Config) *Server {
	listener, err := net.Listen("tcp", config.Address())
	if err != nil {
		log.Fatalw("fault to new gin server", "error", err)
	}
	config.Host, config.Port = xnet.GetHostAndPortByAddr(listener.Addr())
	gin.SetMode(config.Mode)

	info := server.NewInfo(
		server.WithScheme("http"),
		server.WithAddress(config.Host, config.Port),
		server.WithKind(transport.ServiceProvider),
	)
	return &Server{
		Engine:   gin.New(),
		config:   config,
		listener: listener,
		info:     &info,
	}
}

//Upgrade protocol to WebSocket
func (s *Server) Upgrade(ws *WebSocket) gin.IRoutes {
	return s.GET(ws.Pattern, func(c *gin.Context) {
		ws.Upgrade(c.Writer, c.Request)
	})
}

//Register service
func (s *Server) RegisterService(desc *ServiceDesc, service interface{}, middle ...gin.HandlerFunc) {
	if service != nil {
		ht := reflect.TypeOf(desc.HandlerType).Elem()
		st := reflect.TypeOf(service)
		if !st.Implements(ht) {
			log.Fatalf("gin: RegisterService found the handler of type %v that does not satisfy %v", st, ht)
		}
	}
	ss := &server.Service{
		Name:    desc.ServiceName,
		Methods: make([]string, len(desc.Methods)),
	}
	for i, method := range desc.Methods {
		m := method
		s.Handle(m.MethodType, m.Path, func(c *gin.Context) {
			m.Handler(service, c)
		}).Use(middle...)
		ss.Methods[i] = m.Path

	}
	svr.info.Services[desc.ServiceName] = ss
}

// Serve implements server.Init interface.
func (s *Server) Init() error {
	return nil
}

// Serve implements server.Server interface.
func (s *Server) Start() error {
	s.Server = &http.Server{
		Addr:    s.config.Address(),
		Handler: s,
	}
	err := s.Server.Serve(s.listener)
	if err == http.ErrServerClosed {
		log.Warnw("fault to close gin", "address", s.config.Address())
		return nil
	}

	return err
}

// Stop implements server.Server interface
// it will terminate gin server immediately
func (s *Server) Stop() error {
	return s.Server.Close()
}

// GracefulStop implements server.Server interface
// it will stop gin server gracefully
func (s *Server) GracefulStop(ctx context.Context) error {
	return s.Server.Shutdown(ctx)
}

// Info returns server info, used by governor and consumer balancer
func (s *Server) Info() *server.Info {
	return s.info
}
